package pers.fengxu.learnmybatis.config;

import pers.fengxu.learnmybatis.annotation.Select;
import pers.fengxu.learnmybatis.jdbc.MapperStatement;
import pers.fengxu.learnmybatis.proxy.MapperProxy;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.URL;
import java.net.URLDecoder;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class Configure {
    private final Map<String, MapperStatement> mapperStatementMap = new ConcurrentHashMap<>();
    private String basePackage;

    public Configure(String basePackage) {
        this.basePackage = basePackage;
        init();
    }

    public Map<String, MapperStatement> getMapperStatementMap() {
        return mapperStatementMap;
    }

    private void init() {
        try {
            List<Class<?>> classList = listClasses(basePackage);
            for (Class<?> aClass : classList) {
                Method[] declaredMethods = aClass.getDeclaredMethods();
                for (Method declaredMethod : declaredMethods) {
                    Select select = declaredMethod.getAnnotation(Select.class);
                    if (select != null) {
                        String sql = select.value();
                        String id = aClass.getPackageName() + "." + declaredMethod.getName();
                        Class<?> returnType = declaredMethod.getReturnType();
                        MapperStatement mapperStatement = new MapperStatement(sql, returnType, id, declaredMethod.getParameters());
                        mapperStatementMap.put(id, mapperStatement);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private List<Class<?>> listClasses(String basePackage) throws UnsupportedEncodingException, ClassNotFoundException {
        List<Class<?>> classList = new ArrayList<>();
        URL rootUrl = Thread.currentThread().getContextClassLoader().getResource(basePackage.replace(".", "/"));
        Deque<String> st = new ArrayDeque<>();
        st.addLast(URLDecoder.decode(rootUrl.getFile(), "UTF-8"));
        while (!st.isEmpty()) {
            String path = st.removeLast();
            File file = new File(path);
            if (file.exists()) {
                if (file.isDirectory()) {
                    File[] files = file.listFiles();
                    if (files != null && files.length > 0) {
                        Arrays.stream(files).map(File::getAbsolutePath).forEach(st::addLast);
                    }
                } else {
                    String name = file.getName().substring(0, file.getName().lastIndexOf("."));
                    Class<?> clazz = Thread.currentThread().getContextClassLoader().loadClass(basePackage + "." + name);
                    classList.add(clazz);
                }
            }
        }
        return classList;
    }


    public <T> T getMapper(Class<T> clazz) {
        return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, new MapperProxy(this));
    }


}
